package org.maxkey.portal.api.handler;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.maxkey.portal.api.controller.BaseController;
import org.maxkey.portal.core.authentication.CurrentAccount;
import org.maxkey.portal.core.authentication.UsernamePasswordCredential;
import org.maxkey.portal.core.cam.AuthResult;
import org.maxkey.portal.core.cam.Constants;
import org.maxkey.portal.core.cam.CurrentThreadLocal;
import org.maxkey.portal.core.cam.ResultData;
import org.maxkey.portal.core.ticket.UniqueTicketIdGenerator;
import org.maxkey.portal.core.util.JwtUtil;
import org.maxkey.portal.model.UserInfo;
import org.maxkey.portal.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Slf4j
public class WebAuthenticationHandler {


    @Value("${org.maxkey.portal.jwt.publicRsaKey}")
    private String publicRsaKey;

    @Autowired
    private UniqueTicketIdGenerator uniqueTicketIdGenerator;

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private RedisTemplate redisTemplate;

    public AuthResult authentication(final HttpServletRequest request,final UsernamePasswordCredential credential) {
        log.info("authentication username:{}",credential.getId());
        if(credential.validate()){
            String ip= BaseController.getClientIp(request);
            String agent=request.getHeader(Constants.USER_AGENT_HEADER);
            Long time=System.currentTimeMillis();
            //判断是否单点登录
            if(credential.isSso()){
                String ssoUser= JwtUtil.getAccessIssuer(publicRsaKey,credential.getId());
                log.info("JwtUtil access Issuer:{} ",ssoUser);
                if(StringUtils.isEmpty(ssoUser)){
                    return new AuthResult(Constants.VALIDATA_CREDENTIAL);
                }else{
                    credential.setUsername(ssoUser);
                }
            }
            ResultData resultData = userInfoService.findUserByLoginname(credential.getId());
            if(resultData.getCode().longValue()==200){
                UserInfo userinfo= (UserInfo) resultData.getData();
                //如果是单点登录，则不执行密码验证
                if(!credential.isSso()) {
                    if (!userinfo.getLoginpwd().equals(credential.getPassword())) {
                        log.info("authentication username:{} fail", credential.getId());
                        return new AuthResult(Constants.VALIDATA_CREDENTIAL);
                    }
                }
                log.info("----------------------------------------------");

                log.info("authentication ip:{}",ip);
                log.info("authentication time:{}",time);
                log.info("authentication agent:{}",agent);

                //更新用户信息-登录时间-IP-浏览器
                userinfo.setLastLoginIp(ip);
                userinfo.setLastLoginTime(new Date());
                userInfoService.save(userinfo);
                //认证登录后重新更换会话ID

                String newSessionId=getNewSession(request);
                CurrentAccount currentAccount=new CurrentAccount(userinfo.getId(),credential.getId(),userinfo.getName(),ip,agent,time,newSessionId);
                //创建凭证
                String assessToken=uniqueTicketIdGenerator.getNewTicketId(Constants.CURRENT_ACCESS_TOKEN);
                log.info("authentication create token result:{}",assessToken);
                Long killTime=1800L;
                put(assessToken,currentAccount,killTime);
                log.info("authentication put redis key:{},value:{},killTime:{}",assessToken,currentAccount,killTime);
                CurrentThreadLocal.remove();
                //放入当前线程
                CurrentThreadLocal.set(currentAccount);
                log.info("username:{},authentication result OK",credential.getId());
                log.info("----------------------------------------------");
                //返回认证结果
                return new AuthResult(Constants.SUCCESS,killTime,assessToken);
            }
            log.info("username or password is error");
            return new AuthResult(Constants.VALIDATA_CREDENTIAL);
        }else{
            log.info("username and password isEmpty");
            return new AuthResult(Constants.VALIDATA_CREDENTIAL);
        }
    }


    private void put(String key,CurrentAccount account,Long time){
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        try {

            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(account);
            oos.close();
            String value=new String(Base64.encodeBase64(bos.toByteArray()) );
            log.debug("put key:"+key+",value="+value);
            redisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public CurrentAccount getCurrentAccount(String accessToken){
        if(StringUtils.isEmpty(accessToken)){
            return null;
        }
        log.debug("get accessToken:"+accessToken);
        //当前线程获取认证用户，如果没有则从redis获取
        CurrentAccount currentAccount=null;
        try {
            //获取根据session 获取认证对象
            String value= (String) redisTemplate.opsForValue().get(accessToken);
            log.debug("get redis data:"+value);
            if(value!=null){
                ObjectInputStream oos=new ObjectInputStream(new ByteArrayInputStream(Base64.decodeBase64(value.getBytes())));
                currentAccount= (CurrentAccount)oos.readObject();
            }else{
                return null;
            }
        } catch (Exception e) {

        }
        return currentAccount;
    }


    private String getNewSession(final HttpServletRequest request){
        HttpSession oldSession = request.getSession(false);
        if(oldSession != null){
            oldSession.invalidate();  //废除掉登陆前的session
        }
        // 重新创建session,防止登录前后session变化
        request.getSession(true);
        return  request.getSession().getId();
    }
}
